<?php

class WxpayController extends Controller
{
    protected $_log_key = null;

    public function init()
    {
        $this->user = app()->wxUser->getDbData();
    }

    protected function _log($info = null, $file_name = 'newretail_wxcallback')
    {
        if($this->_log_key === null){
            $this->_log_key = create_uuid();
        }

        $log_file = dirname(__FILE__) . '/logs/'.$file_name.'.'.date('ymd').'.log';

        $info = empty($info)? '' : $info;
        $info = date('Y-m-d H:i:s - ')  . '('.$this->_log_key.')' . $info . "\n";

        $handler = fopen($log_file, 'a+');
        fputs($handler, $info);
        fclose($handler);
    }

    protected function _MD5Sign($data, $token)
    {
        //拿到传过来值不为空的数据
        $data = \array_filter($data);
        ksort($data);
        $str = implode('', $data);
        return md5($str . '&' . $token);
    }

    /*public function actionAuthToken()
    {
        $str = $_GET['echostr'];

        echo $str;
    }*/

    public $layout = '//layouts/basic';

    const ERROR_CODE = 500;

    protected function _setError($msg = '系统繁忙')
    {
        return array(
            'code' => self::ERROR_CODE,
            'msg' => $msg,
        );
    }

    protected function _notifySign($str, $token)
    {
        $arr = json_decode($str, true);
        if(!empty($arr['out_trade_no'])){
            $out_trade_no = (!empty($arr['out_trade_no'])?$arr['out_trade_no']:'');
        }
        return strtolower(md5(
            $str.'&'.$out_trade_no.$token
        ));
    }

    protected function _beforePay($post_data = array(), $allpay_biz_channel = '', $allpay_biz_type = '')
    {
        $record_id = create_uuid();

        $data = array_merge(array(
            'notify_url' => '',
            'merchant_id' => '',
            'submch_accnt_no' => '',
            'total_amount' => 0,
            'callback_url' => '',
            'trade_no' => '',
            'sign' => '',
        ), $post_data);
        $data['total_amount'] = $data['total_amount']*1;
        $this->_log("postData(".$record_id.")".\json_encode($post_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'before_pay');
        if($data['total_amount']*1 <= 0){
            return $this->_setError('订单金额有误');
        }

        $sign = $data['sign'];
        unset($data['sign']);

        $newretail_merchant = NewretailMerchant::model()->findBySql("
            SELECT id_newretail_merchant, merchant_id, md5_token FROM newretail_merchant WHERE merchant_id = :merchant_id
        ", array(
            ':merchant_id' => $data['merchant_id'],
        ));

        if(empty($newretail_merchant)){
            return $this->_setError('merchant_id不存在');
        }

        $sub_merchant = Merchant::model()->findByAttributes(array(
            'id_newretail_merchant' => $newretail_merchant->id_newretail_merchant,
            'sub_merchant_no' => $data['submch_accnt_no'],
        ));

        if(empty($sub_merchant)){
            return $this->_setError('子商户信息错误');
        }

        if($this->_MD5Sign($data , $newretail_merchant->md5_token) !== $sign){
            return $this->_setError('签名错误');
        }

        $pay_info_error = null;
        $pay_info = null;

        $tradeDetail = TradeDetail::model()->findByAttributes(array(
            'out_tradeno' => $data['trade_no'],
            'id_merchant' => $sub_merchant->id_merchant,
        ));

        if(!empty($tradeDetail) && $tradeDetail->trade_status == TradeDetail::TRADE_STATUS_SUCCESS){
            return $this->_setError('订单已存在');
        }

        if(empty($tradeDetail)){
            $tradeDetail = new TradeDetail();
        }

        $tradeDetail->id_merchant = $sub_merchant->id_merchant;
        $tradeDetail->amount = $data['total_amount'] * 100;
        $tradeDetail->amount_due = $data['total_amount'] * 100;
        $tradeDetail->out_tradeno = empty($data['trade_no']) ? 'SW'.$tradeDetail->genOuttradeno() : $data['trade_no'];
        $tradeDetail->trade_status = TradeDetail::TRADE_STATUS_STARTUP;
        $tradeDetail->allpay_merchantno = app()->params['newretail']['allpay']['allpay_merchant'];
        $tradeDetail->allpay_biz_channel = $allpay_biz_channel;
        $tradeDetail->allpay_biz_type = $allpay_biz_type;
        $tradeDetail->notify_url = $data['notify_url'];
        $tradeDetail->createtime = date('Y-m-d H:i:s');
        $tradeDetail->id_newretail_user = $this->user->id_user;
        $tradeDetail->id_newretail_merchant = $newretail_merchant->id_newretail_merchant;

        $tradeDetail->feerate = $sub_merchant->feerate;
        $tradeDetail->fee = round($data['total_amount'] * $sub_merchant->feerate / 10000, 2) * 100;
        $tradeDetail->fee = $tradeDetail->fee < 1? 0 : $tradeDetail->fee;
        $tradeDetail->save(false);

        return array_merge($data,array(
            'id_tradedetail' => $tradeDetail->id_tradedetail,
            'out_tradeno' => $tradeDetail->out_tradeno,
            'record_id' => $record_id,
            'body' => empty($data['body']) ? '自助购物' : $data['body'] ,
        ));
    }

    public function actionPayfaillog()
    {
        if(!empty($_POST)){
            $msg = json_encode($_POST, JSON_UNESCAPED_UNICODE);

            $this->_log("info({$_POST['record_id']}): " . $msg,'wxpay_fail');
            return true;
        }
    }

    public function actionJspay()
    {
        $result = $this->_beforePay($_POST, TradeDetail::BIZ_CHANNEL_WXPAY, TradeDetail::BIZ_TYPE_WX_JSPAY);

        if(!empty($result['code']) && $result['code'] == self::ERROR_CODE){
            return $this->render('error', array(
                'message' => $result['msg'],
            ));
        }

        $paydata = json_encode(array(
            'version' => '2.0',
            'merchant_id' => app()->params['newretail']['allpay']['allpay_merchant'],
            'term_id' => '',
            'timestamp' => date('YmdHis'),
            'biz_type' => 'wx.jspay',
            'biz_content' => array(
                'trade_no' => $result['out_tradeno'],
                'sub_appid' => app()->params['newretail']['allpay']['sub_appid'],
                'openid' => $this->user->wx_openid,
                'total_amount' => $result['total_amount'],
                'attach' => $result['id_tradedetail'],
                'body' => $result['body'],
                'notify_url' => app()->params['newretail']['allpay']['wx_notify_url'],
                'time_expire' => date('YmdHis', time() + 360)
            ),
            'sign_type' => 'MD5'
        ));

        $token = app()->params['newretail']['allpay']['token'];
        $sign = SignMd5Helper::genSign($paydata, $token);

        $curl = new Curl();
        $this->_log("Request({$result['record_id']}): " . json_encode(array(
                'data' => $paydata,
                'sign' => $sign
            )),'wxpay');

        $ret = $curl->postRawExe(app()->params['newretail']['allpay']['api_url'], array(
            'data' => $paydata,
            'sign' => $sign
        ));

        $this->_log("Response({$result['record_id']}): " .$ret, 'wxpay');

        $ret = json_decode($ret, true);
        $ret_data = json_decode($ret['data'], true);

        if(!empty($ret_data['code'] ) && $ret_data['code']  == '100'){
            $ret_biz_content = $ret_data['biz_content'];
            $pay_info = $ret_biz_content['pay_info'];

            return $this->render('wxpay', array(
                'pay_info' => $pay_info,
                'callback_url' => $result['callback_url'],
                'record_id' => $result['record_id'],
                'trade_no' => $result['out_tradeno'],
            ));
        }else{
            return $this->render('error',array(
                'message' => empty($ret['message'])?'payinfo not found':$ret['message'],
            ));
        }
    }


    protected function _verfiyNotiSign($data, $sign='')
    {
        $md5_token = app()->params['newretail']['allpay']['token'];

        $data_arr = \json_decode($data, true);

        $to_verfiy_sign = \strtolower(md5(
            $data . '&'. $data_arr['out_trade_no']. $md5_token
        ));

        if($sign === $to_verfiy_sign){
            return true;
        }
        return false;
    }

    private function _paynotify($postStr = '' ,$log_id = '')
    {
        if(!empty($postStr)){
            $postObj = json_decode($postStr, true);

            $data = json_decode($postObj['data'], true);

            $id_tradedetail = $data['attach'];

            $tradeDetail = TradeDetail::model()->findByAttributes(array(
                'id_tradedetail' => $id_tradedetail
            ));

            if(!empty($tradeDetail) && $this->_verfiyNotiSign($postObj['data'], $postObj['sign'])){

                $sub_merchant = Merchant::model()->findByAttributes(array(
                    'id_merchant' => $tradeDetail->id_merchant,
                ));
                $newretail_merchant = NewretailMerchant::model()->findByAttributes(array(
                    'id_newretail_merchant' => $tradeDetail->id_newretail_merchant,
                ));

                $system_flag = false;

                if($tradeDetail->trade_status != TradeDetail::TRADE_STATUS_SUCCESS){

                    $tradeDetail->channel_tradeno = $data['transaction_id'];
                    $tradeDetail->trade_status = TradeDetail::TRADE_STATUS_SUCCESS;
                    $tradeDetail->paytime = date('Y-m-d H:i:s');
                    $tradeDetail->bank_type = empty($data['bank_type']) ?'0':$data['bank_type'];
                    if($tradeDetail->save(false)){

                        if($newretail_merchant && $sub_merchant){

                            $connection = app()->db;
                            $transaction=$connection->beginTransaction();
                            try {
                                $subMerchantUpdateRes = app()->db->createCommand('
                                UPDATE merchant set remain_cash = remain_cash + :remain_cash_value, total_remain_cash = total_remain_cash + :total_remain_cash_value WHERE id_merchant = :id_merchant 
                            ')->execute(array(
                                    ':remain_cash_value' => $tradeDetail->amount - $tradeDetail->fee,
                                    ':total_remain_cash_value' => $tradeDetail->amount,
                                    ':id_merchant' => $sub_merchant->id_merchant,
                                ));

                                $merchantUpdateRes = app()->db->createCommand('
                                UPDATE newretail_merchant set remain_cash = remain_cash + :remain_cash_value, total_remain_cash = total_remain_cash + :total_remain_cash_value WHERE id_newretail_merchant = :id_newretail_merchant
                            ')->execute(array(
                                    ':remain_cash_value' => $tradeDetail->amount - $tradeDetail->fee,
                                    ':total_remain_cash_value' => $tradeDetail->amount,
                                    ':id_newretail_merchant' => $newretail_merchant->id_newretail_merchant,
                                ));

                                $balance_change_time = date('Y-m-d H:i:s');

                                $subMerBalanceDetailModel = new BalanceDetail();
                                $subMerBalanceDetailModel->type = BalanceDetail::ADD_TYPE;
                                $subMerBalanceDetailModel->cash = $tradeDetail->amount;
                                $subMerBalanceDetailModel->before_remain_cash = $sub_merchant->remain_cash;
                                $subMerBalanceDetailModel->after_remain_cash = $sub_merchant->remain_cash + $tradeDetail->amount;
                                $subMerBalanceDetailModel->desc = BalanceDetail::ADD_DESC;
                                $subMerBalanceDetailModel->create_time = $balance_change_time;
                                $subMerBalanceDetailModel->id_merchant = $sub_merchant->id_merchant;
                                $subAddRes = $subMerBalanceDetailModel->save(false);

                                $reduce_flaf = true;
                                //手续费
                                if($tradeDetail->fee > 0){

                                    $balanceDetailModel = new BalanceDetail();
                                    $balanceDetailModel->id_merchant = $sub_merchant->id_merchant;
                                    $balanceDetailModel->type = BalanceDetail::REDUCE_TYPE;
                                    $balanceDetailModel->cash = $tradeDetail->fee;
                                    $balanceDetailModel->before_remain_cash = $subMerBalanceDetailModel->after_remain_cash;
                                    $balanceDetailModel->after_remain_cash = $subMerBalanceDetailModel->after_remain_cash - $tradeDetail->fee;
                                    $balanceDetailModel->desc = BalanceDetail::REDUCE_DESC;
                                    $balanceDetailModel->create_time = $balance_change_time;
                                    $subFeeRes = $balanceDetailModel->save(false);

                                    if(!$subFeeRes){
                                        $reduce_flaf = false;
                                    }
                                }
                                if($subAddRes && $subMerchantUpdateRes && $merchantUpdateRes && $reduce_flaf){
                                    $transaction->commit();
                                    $system_flag = true;
                                }else{
                                    $transaction->rollBack();
                                }
                            } catch(Exception $e) {
                                $this->_log($e->getMessage());
                                $transaction->rollBack();
                            }
                        }
                    }
                }else{
                    $system_flag = true;
                }

                $out_system_flag = false;
                if($tradeDetail->notify_url ){
                    $postObj['sign'] = $this->_notifySign($postObj['data'], $newretail_merchant->md5_token);

                    $curl = new Curl();
                    $res = $curl->postExe($tradeDetail->notify_url, $postObj);

                    $this->_log("(notify_response{".$log_id."})".$res);

                    if(is_string($res) && strtolower($res) === 'success'){
                        $tradeDetail->notify_url = '';
                        $tradeDetail->save();
                        $out_system_flag = true;
                    }
                }else{
                    $out_system_flag = 'true';
                }

                if($system_flag && $out_system_flag){
                    return true;
                }
            }
        }
        return false;
    }

    public function actionWxcallback()
    {
        try{

            $log_id = create_uuid();
            $postStr = file_get_contents("php://input");

            $this->_log("(receive{".$log_id."})".$postStr);

            $res = $this->_paynotify($postStr, $log_id);
            if($res){
                echo 'success';
            }else{
                echo 'fail';
            }

        }catch (Exception $e){
            $this->_log($e->getMessage());
            echo 'fail';

        }
    }

    public function accessRules()
    {
        $isLoginExpression = 'false';

        if(app()->wxUser->getIsLogin()){
            $isLoginExpression = 'true';
            $isLoginRedirect = null;
        }else{
            $isLoginExpression = 'false';

            $isLoginRedirect = url('/newretailwxauth/wxlogin', array(
                'callback_landing' => app()->request->getUrl(),
                'post_data' => json_encode($_POST),
            ));
        }

        return array(
            array(
                'allow',
                'actions' => array('jspay'),
                'expression' => $isLoginExpression,
                'redirect' => $isLoginRedirect
            ),

            array(
                'allow',
                'users' => array('*'),
            )
        );
    }
}